package snafu.implementation.worlds;

import java.awt.geom.Point2D;
import java.util.HashSet;
import java.util.Set;

import snafu.framework.agents.Agent;
import snafu.framework.exceptions.SnafuException;
import snafu.framework.worlds.Position;
import snafu.framework.worlds.Tile;
import snafu.framework.worlds.WorldMap;
import snafu.framework.worlds.WorldMapPanel;

public class SquareWorldMap extends WorldMap {
	public SquareWorldMap(int width, int height) {
		super(width, height);
	}
	
	@Override
	public WorldMap getPerceptionFor(Agent agent) {
		// get the position for the object
		Position position = getPosition(agent);

		// create the perception map
		int size = PERCEPTION_WIDTH * 2 + 1;
		WorldMap perception = new SquareWorldMap(size, size);
		for (int y = 0; y < size; y++) 
			for (int x = 0; x < size; x++) {
				// get the original x and y value
				int orgX = position.x - PERCEPTION_WIDTH + x;
				int orgY = position.y - PERCEPTION_WIDTH + y;
				
				// get the old tile and create a new one
				Tile tile = get(new Position(orgX, orgY));
				if (tile == null) {
					continue;
				}
				
				// add the tile to the perception
				perception.set(new Position(x, y), createTileViewFor(tile, agent, new Position(x,y)));
			}
		
		return perception;
	}
	
	@Override
	public Set<Tile> getNeighborhood(Position from) {
		HashSet<Tile> tiles = new HashSet<Tile>();
		Tile[] possibleTiles = new Tile[] {
				get(new Position(from.x - 1, from.y)),
				get(new Position(from.x, from.y - 1)),
				get(new Position(from.x + 1, from.y)),
				get(new Position(from.x, from.y + 1)),
		};
		for (Tile tile : possibleTiles) {
			if (tile != null)
				tiles.add(tile);
		}
		return tiles;
	}
	
	@Override
	public Tile getTile(double x, double y) {
		return get(new Position((int) x, (int) y));
	}
	
	@Override
	public Tile getFrontTile(Position from, Integer direction) {
		// get current position
		int x = from.x;
		int y = from.y;
		
		switch (direction) {
		// north
		case 0:
			y = y - 1;
			break;
		// east
		case 1:
			x = x + 1;
			break;
		// south
		case 2:
			y = y + 1;
			break;
		// west
		case 3:
			x = x - 1;
			break;
		// something else
		default:
			throw new SnafuException("unknown direction " + direction);
		}
		
		return get(new Position(x, y));
	}
	
	// Direction: 0 = North, 1 = East, 2 = South, 3 = West
	@Override
	public Integer getNextLeftDirection(Integer direction) {
		return (direction + 3) % 4; 
	}
	
	@Override
	public Integer getNextRightDirection(Integer direction) {
		return (direction + 1) % 4;
	}
	
	@Override
	public String output() {
		String o = "";
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) 
				o += tiles[x][y] == null ? ". " : tiles[x][y] + " ";
			o += "\n";
		}
		return o;
	}

	@Override
	public Point2D convertPositionToPoint(Position position) {
		return new Point2D.Double(position.x, position.y);
	}
	
	@Override
	public Point2D getDimension() {
		return new Point2D.Double(width, height);
	}
	
	@Override
	public Point2D getTileSize() {
		return new Point2D.Double(1, 1);
	}
	
	@Override
	public WorldMapPanel getRepresentation() {
		return new SquareWorldMapPanel(this);
	}
}
